#include "ObjectManager.h"

#include "GameObject.h"
#include "Augment.h"
#include "MovingObject.h"
#include "../SGD Wrappers/SGD_Geometry.h"
/**************************************************************/
// AddEntity
//	- store the Object into the specified bucket
//	- the Object Manager holds a reference to the Object
void ObjectManager::AddObject(GameObject* pObject, unsigned int bucket)
{
	// Expand the table?
	if (bucket >= m_tObjects.size())
	{
		m_tObjects.resize(bucket + 1);
		//Increase bucket count to largest bucket
		m_iBucketCount = bucket;
	}
		

	// Append the entity into the specified vector
	m_tObjects[bucket].push_back(pObject);

	// Hold a reference to keep the entity in memory
	if (pObject != nullptr)
		pObject->AddRef();
}


/**************************************************************/
// RemoveObject
//	- remove the Object from the specified bucket
//	- release the reference to the Object
void ObjectManager::RemoveObject(GameObject* pObject, unsigned int bucket)
{
	// Try to find the Object
	ObjectVector& vec = m_tObjects[bucket];
	for (unsigned int i = 0; i < vec.size(); i++)
	{
		if (vec[i] == pObject)
		{
			// Remove the Object
			vec.erase(vec.begin() + i);
			pObject->Release();
			break;
		}
	}

	//if bucket is empty reduce size of bucket count.
	if (m_tObjects[bucket].size() == 0)
		--m_iBucketCount;

}


/**************************************************************/
// RemoveObject
//	- remove & release the Object from any bucket
void ObjectManager::RemoveObject(GameObject* pObject)
{
	// Try to find the Object in any buckect
	for (unsigned int bucket = 0; bucket < m_tObjects.size(); bucket++)
	{
		ObjectVector& vec = m_tObjects[bucket];
		for (unsigned int i = 0; i < vec.size(); i++)
		{
			if (vec[i] == pObject)
			{
				// Remove the Object
				vec.erase(vec.begin() + i);
				pObject->Release();
				return;
			}
		}
	}
}


/**************************************************************/
// RemoveAll
//	- remove all Objects from a specific bucket
void ObjectManager::RemoveAllFromBucket(unsigned int unBucket)
{
	// Lock the iterator
	m_bIterating = true;
	{
		// Release the reference to EVERY Object
		ObjectVector& vec = m_tObjects[unBucket];
		for (unsigned int i = 0; i < vec.size(); i++)
		{
			vec[i]->Release();
			vec[i] = nullptr;
		}

		vec.clear();
	}
	// Unlock the iterator
	m_bIterating = false;
}


/**************************************************************/
// RemoveAll
//	- release each Object in the table
void ObjectManager::RemoveAll(void)
{
	// Lock the iterator
	m_bIterating = true;
	{
		// Release every Object
		for (unsigned int bucket = 0; bucket < m_tObjects.size(); bucket++)
		{
			ObjectVector& vec = m_tObjects[bucket];
			for (unsigned int i = 0; i < vec.size(); i++)
			{
				vec[i]->Release();
				vec[i] = nullptr;
			}
		}
	}
	// Unlock the iterator
	m_bIterating = false;


	// Collapse the table
	m_tObjects.clear();
}


/**************************************************************/
// UpdateAll
//	- update each Object in the table
void ObjectManager::UpdateAll(float elapsedTime)
{
	// Lock the iterator
	m_bIterating = true;
	{
		// Update every Object
		for (unsigned int bucket = 0; bucket < m_tObjects.size(); bucket++)
		{
			ObjectVector& vec = m_tObjects[bucket];
			for (unsigned int i = 0; i < vec.size(); i++)
				vec[i]->Update(elapsedTime);
		}
	}
	// Unlock the iterator
	m_bIterating = false;
}


/**************************************************************/
// RenderAll
//	- render each Object in the table
void ObjectManager::RenderAll(void)
{
	// Lock the iterator
	m_bIterating = true;
	{
		// Render every Object
		for (unsigned int bucket = 0; bucket < m_tObjects.size(); bucket++)
		{
			ObjectVector& vec = m_tObjects[bucket];
			for (unsigned int i = 0; i < vec.size(); i++)
				vec[i]->Render();
		}
	}
	// Unlock the iterator
	m_bIterating = false;
}


/**************************************************************/
// CheckCollisions
//	- check collision between the Objects within the two buckets
void ObjectManager::CheckCollisions(unsigned int bucket1, unsigned int bucket2)
{	
	// Quietly validate the parameters
	if (bucket1 >= m_tObjects.size()
		|| bucket2 >= m_tObjects.size()
		|| m_tObjects[bucket1].size() == 0
		|| m_tObjects[bucket2].size() == 0)
		return;

	// Lock the iterator
	m_bIterating = true;
	{
		// Are they different buckets?
		if (bucket1 != bucket2)
		{
			// Which bucket is smaller?
			//	should be the outer loop for less checks (n0)*(n1+1) + 1
			ObjectVector* pVec1 = &m_tObjects[bucket1];
			ObjectVector* pVec2 = &m_tObjects[bucket2];

			if (pVec2->size() < pVec1->size())
			{
				ObjectVector* pTemp = pVec1;
				pVec1 = pVec2;
				pVec2 = pTemp;
			}

			ObjectVector& vec1 = *pVec1;
			ObjectVector& vec2 = *pVec2;


			// Iterate through the smaller bucket
			for (unsigned int i = 0; i < vec1.size(); i++)
			{
				// Iterate through the larger bucket
				for (unsigned int j = 0; j < vec2.size(); j++)
				{
					// Ignore self-collision
					if (vec1[i] == vec2[j])
						continue;

					// Local variables help with debugging
					SGD::Rectangle rObject1 = vec1[i]->GetRect();
					SGD::Rectangle rObject2 = vec2[j]->GetRect();

					SGD::Rectangle rObject1Active = vec1[i]->GetActiveRect();
					SGD::Rectangle rObject2Active = vec2[j]->GetActiveRect();

					// Check for collision between the Objects
					if (rObject1.IsIntersecting(rObject2) == true)
					{
						// Both objects handle collision
						vec1[i]->HandleCollision(vec2[j]);
						vec2[j]->HandleCollision(vec1[i]);
					}
					else if (rObject1Active != SGD::Rectangle(0, 0, 0, 0) && rObject1.IsIntersecting(rObject2Active) == true)
					{
						// Only the object getting attacked handles collision
						vec1[i]->HandleCollision(vec2[j]);
					}
					else if (rObject2Active != SGD::Rectangle(0, 0, 0, 0) && rObject2.IsIntersecting(rObject1Active) == true)
					{
						// Only the object getting attacked handles collision
						vec2[j]->HandleCollision(vec1[i]);
					}
				}
			}
		}
		else // bucket1 == bucket2
		{
			ObjectVector& vec = m_tObjects[bucket1];

			// Optimized loop to ensure objects do not collide with
			// each other twice
			for (unsigned int i = 0; i < vec.size() - 1; i++)
			{
				for (unsigned int j = i + 1; j < vec.size(); j++)
				{
					// Ignore self-collision
					if (vec[i] == vec[j])
						continue;

					// Local variables help with debugging
					SGD::Rectangle rObject1 = vec[i]->GetRect();
					SGD::Rectangle rObject2 = vec[j]->GetRect();

					// Check for collision between the Objects
					if (rObject1.IsIntersecting(rObject2) == true)
					{
						// Both objects handle collision
						vec[i]->HandleCollision(vec[j]);
						vec[j]->HandleCollision(vec[i]);
					}
				}
			}
		}
	}
	// Unlock the iterator
	m_bIterating = false;
}

GameObject* ObjectManager::ReturnObject(unsigned int bucket, unsigned int index)
{
	if (bucket >= m_tObjects.size())
		return nullptr;
	ObjectVector& vec = m_tObjects[bucket];
	if (vec.size() > 0 && index < vec.size())
		return vec[index];
	return nullptr;
}
bool ObjectManager::IsEmpty(unsigned int bucket)
{
	if (bucket < m_tObjects.size())
	{
		ObjectVector& vec = m_tObjects[bucket];
		if (vec.capacity() > 0)
			return false;
	}
	return true;
}
void ObjectManager::CleanUpAugments()
{
	if (m_tObjects.size() < 5) return;
	ObjectVector& vec = m_tObjects[6];
	if (vec.size() == 0) return;
	for (size_t i = 0; i < vec.size(); i++)
	{
		Augment * temp = (Augment*)vec[i];
		temp->SetpickedUp(true);
		temp->SetPosition({ -100, -100 });
	}
}
GameObject* ObjectManager::ReturnClosestEnemy()
{
	if (m_tObjects.size() < 4) return nullptr;
	ObjectVector& daPlayerbucket = m_tObjects[2];
	ObjectVector& daEnemiesbucket = m_tObjects[3];
	if (daEnemiesbucket.size() == 0) return nullptr;
	MovingObject* playah = (MovingObject*)daPlayerbucket[0];
	float closestenemy = 800;
	int enemyatthisindex = 0;
	for (size_t i = 0; i < daEnemiesbucket.size(); i++)
	{
		MovingObject* tempenemy = (MovingObject*)daEnemiesbucket[i];
		SGD::Vector dist = playah->GetPosition() - tempenemy->GetPosition();
		if (dist.ComputeLength() < closestenemy)
		{
			closestenemy = dist.ComputeLength();
			enemyatthisindex = i;
		}
	}

	if (closestenemy < 800)
		return daEnemiesbucket[enemyatthisindex];
	else
		return nullptr;
}
